package restdom.dao.impl;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.SessionFactoryUtils;

import restdom.dao.GenericDao;
import restdom.finder.FinderSupport;

public class GenericDaoHibernateImpl<T, ID extends Serializable> implements
		GenericDao<T, ID>, FinderSupport {

	private SessionFactory sessionFactory;

	private Class<T> persistentClass;

	public GenericDaoHibernateImpl(Class<T> clazz) {
		this.persistentClass = clazz;
	}

	@SuppressWarnings("unchecked")
	public ID create(T object) {
		return (ID) getSession().save(object);
	}

	public T read(ID id) {
		@SuppressWarnings("unchecked")
		T object = (T) getSession().get(persistentClass, id);
		
		if (object == null) {
			throw new ObjectRetrievalFailureException(persistentClass, id);
		}

		return object;
	}

	public void update(T object) {
		getSession().update(object);
	}

	public void delete(T object) {
		getSession().delete(object);
	}

	@SuppressWarnings("unchecked")
	public List<T> find(String finder, Object... args) {
		final Query namedQuery = prepareQuery(getQueryName(finder), args);
		return (List<T>) namedQuery.list();
	}

	protected String getQueryName(String query) {
		return persistentClass.getSimpleName() + "." + query;
	}
	
	protected Query prepareQuery(String queryName, Object[] args) {
		final Query namedQuery = getSession().getNamedQuery(queryName);
		String[] namedParameters = namedQuery.getNamedParameters();
		if (namedParameters.length == 0) {
			setPositionalParams(args, namedQuery);
		} else {
			setNamedParams(namedParameters, args, namedQuery);
		}
		return namedQuery;
	}

	protected void setPositionalParams(Object[] queryArgs, Query namedQuery) {
		if (queryArgs != null) {
			for (int i = 0; i < queryArgs.length; i++) {
				Object arg = queryArgs[i];
				namedQuery.setParameter(i, arg);
			}
		}
	}

	protected void setNamedParams(String[] namedParameters, Object[] queryArgs,
			Query namedQuery) {
		if (queryArgs != null) {
			for (int i = 0; i < queryArgs.length; i++) {
				Object arg = queryArgs[i];
				if (arg instanceof Collection) {
					namedQuery.setParameterList(namedParameters[i],
							(Collection) arg);
				} else {
					namedQuery.setParameter(namedParameters[i], arg);
				}
			}
		}
	}

	public Session getSession() {
		boolean allowCreate = true;
		return SessionFactoryUtils.getSession(sessionFactory, allowCreate);
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
}
